package au.com.handsunited.motherofalldaos.dao.jpa;

import java.lang.reflect.ParameterizedType;
import java.util.Iterator;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import au.com.handsunited.motherofalldaos.dao.BaseDao;
import au.com.handsunited.paginationutils.JpaPage;
import au.com.handsunited.paginationutils.Order;
import au.com.handsunited.paginationutils.Page;

/**
 * @author arunc
 * 
 */
public abstract class AbstractBaseJpaDao<T> implements BaseDao<T> {

	private final Class<T> type;

	@PersistenceContext(unitName = "bhajanswarupa")
	protected EntityManager entityManager;

	private static Logger logger = LoggerFactory
			.getLogger(AbstractBaseJpaDao.class);

	/**
	 * 
	 */
	@SuppressWarnings("unchecked")
	public AbstractBaseJpaDao() {
		super();
		type = (Class<T>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
	}

	/**
	 * @param type
	 */
	public AbstractBaseJpaDao(Class<T> type) {
		super();
		this.type = type;
	}

	/**
	 * @see au.com.handsunited.bhajanswarupa.dao.BaseDao#create(au.com.handsunited.bhajanswarupa.model.MotherOfAllEntities)
	 */
	@Override
	public T create(T newInstance) {
		return entityManager.merge(newInstance);
	}

	/**
	 * @see au.com.handsunited.bhajanswarupa.dao.BaseDao#delete(au.com.handsunited.bhajanswarupa.model.MotherOfAllEntities)
	 */
	@Override
	public void delete(T persistentObject) {

		entityManager.remove(persistentObject);
	}

	/**
	 * @see au.com.handsunited.bhajanswarupa.dao.BaseDao#findAll()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public Page<T> findAll() {

		Query query = entityManager.createQuery(createQueryString());

		return new JpaPage<T>(query);
	}

	/**
	 * @see au.com.handsunited.bhajanswarupa.dao.BaseDao#findAll(int, int)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public Page<T> findAll(int pageNumber, int pageSize) {

		Query query = entityManager.createQuery(createQueryString());
		return new JpaPage<T>(query, pageNumber, pageSize);
	}

	/**
	 * @see au.com.handsunited.bhajanswarupa.dao.BaseDao#findAll(java.util.List)
	 */
	@Override
	public Page<T> findAll(List<Order> orders) {

		Query query = entityManager.createQuery(createQueryString(orders));
		return new JpaPage<T>(query);
	}

	/**
	 * @see au.com.handsunited.bhajanswarupa.dao.BaseDao#findAll(au.com.handsunited.paginationutils.Order,
	 *      int, int)
	 */
	@Override
	public Page<T> findAll(Order orderBy, int pageNumber, int pageSize) {

		Query query = entityManager.createQuery(createQueryString(orderBy));
		return new JpaPage<T>(query);
	}

	/**
	 * @see au.com.handsunited.bhajanswarupa.dao.BaseDao#findAll(au.com.handsunited.paginationutils.Order)
	 */
	@Override
	public Page<T> findAll(Order order) {
		Query query = entityManager.createQuery(createQueryString(order));
		return new JpaPage<T>(query);
	}

	/**
	 * @see au.com.handsunited.bhajanswarupa.dao.BaseDao#findByExample(au.com.handsunited.bhajanswarupa.model.MotherOfAllEntities,
	 *      int, int)
	 */
	@Override
	public Page<T> findByExample(T example, int pageNumber, int pageSize) {
		// TODO Auto-generated method stub
		return findAll(pageNumber, pageSize);
	}

	/**
	 * @see au.com.handsunited.bhajanswarupa.dao.BaseDao#findByExample(au.com.handsunited.bhajanswarupa.model.MotherOfAllEntities,
	 *      au.com.handsunited.paginationutils.Order, int, int)
	 */
	@Override
	public Page<T> findByExample(T example, Order order, int pageNumber,
			int pageSize) {
		// TODO Auto-generated method stub
		return findAll(pageNumber, pageSize);
	}

	/**
	 * @see au.com.handsunited.bhajanswarupa.dao.BaseDao#findByExample(au.com.handsunited.bhajanswarupa.model.MotherOfAllEntities,
	 *      java.lang.String[], int, int)
	 */
	@Override
	public Page<T> findByExample(T exampleInstance, String[] excludeProperty,
			int pageNumber, int pageSize) {
		// TODO Auto-generated method stub
		return findAll(pageNumber, pageSize);
	}

	/**
	 * @see au.com.handsunited.bhajanswarupa.dao.BaseDao#findByExample(au.com.handsunited.bhajanswarupa.model.MotherOfAllEntities,
	 *      java.lang.String[], java.util.List, int, int)
	 */
	@Override
	public Page<T> findByExample(T exampleInstance, String[] excludeProperty,
			List<Order> orderByClause, int pageNumber, int pageSize) {
		// TODO Auto-generated method stub
		return findAll(pageNumber, pageSize);
	}

	/**
	 * @see au.com.handsunited.bhajanswarupa.dao.BaseDao#findByExample(au.com.handsunited.bhajanswarupa.model.MotherOfAllEntities,
	 *      java.lang.String[], java.util.List)
	 */
	@Override
	public Page<T> findByExample(T exampleInstance, String[] excludeProperty,
			List<Order> orderByClause) {
		// TODO Auto-generated method stub
		return findAll();
	}

	/**
	 * @see au.com.handsunited.bhajanswarupa.dao.BaseDao#findByExample(au.com.handsunited.bhajanswarupa.model.MotherOfAllEntities,
	 *      java.lang.String[])
	 */
	@Override
	public Page<T> findByExample(T exampleInstance, String[] excludeProperty) {
		// TODO Auto-generated method stub
		return findAll();
	}

	/**
	 * @see au.com.handsunited.bhajanswarupa.dao.BaseDao#findByExample(au.com.handsunited.bhajanswarupa.model.MotherOfAllEntities)
	 */
	@Override
	public Page<T> findByExample(T example) {
		// TODO Auto-generated method stub
		return findAll();
	}

	/**
	 * @see au.com.handsunited.bhajanswarupa.dao.BaseDao#read(java.lang.Long)
	 */
	@Override
	public T read(Long id) {

		return entityManager.find(type, id);
	}

	/**
	 * @see au.com.handsunited.bhajanswarupa.dao.BaseDao#update(au.com.handsunited.bhajanswarupa.model.MotherOfAllEntities)
	 */
	@Override
	public T update(T transientObject) {

		return entityManager.merge(transientObject);
	}

	/**
	 * @param entityManager
	 *            the entityManager to set
	 */
	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}

	/**
	 * 
	 * @return
	 */
	private String createQueryString() {
		return "SELECT t FROM " + type.getName() + " t";
	}

	/**
	 * 
	 * @param order
	 * @return
	 */
	private String createQueryString(Order order) {
		String queryString = createQueryString();
		if (order != null) {
			queryString += " ORDER BY t." + order;
		}
		return queryString;
	}

	/**
	 * 
	 * @param orders
	 * @return
	 */
	private String createQueryString(List<Order> orders) {

		String queryString = createQueryString();

		if (orders == null || orders.isEmpty()) {
			return queryString;
		} else {
			queryString += " ORDER BY";
			for (Iterator<Order> iter = orders.iterator(); iter.hasNext();) {
				Order order = iter.next();
				queryString += " t." + order;
				if (iter.hasNext()) {
					queryString += ",";
				}
			}
			return queryString;
		}
	}

}
